/**
 *  Copyright (c) 2011, IPC Studio, USTC, All rights reserved
 *  FileName: TxtEncoder.cpp
 *  Author: LewGate@gmail.com
 *  DateCreated: 2011-8-8
 */

#include "../include/TxtEncoder.h"

LIBLYRICS_NAMESPACE_BEGIN

TxtEncoder::TxtEncoder()
{
	m_pbuffer = NULL;
}

TxtEncoder::~TxtEncoder()
{
	if(m_pbuffer)
		delete m_pbuffer;
}

bool TxtEncoder::SetBuffer(char* pbuffer, int size)
{
	m_pbuffer = new char[size];
	memcpy(m_pbuffer, pbuffer, size);
	return true;
}

char* TxtEncoder::Convert(int sourceCodepage, int targetCodepage) 
{
	int unicodeLen, targetLen;
	wchar_t *pUnicode; 
	char* pTargetData = NULL; 

	unicodeLen = MultiByteToWideChar(sourceCodepage, 0, m_pbuffer, -1, NULL, 0); 
	pUnicode = (wchar_t *)malloc((unicodeLen + 1) * sizeof(wchar_t)); 
	memset(pUnicode, 0, (unicodeLen+1) * sizeof(wchar_t)); 
	MultiByteToWideChar(sourceCodepage, 0, m_pbuffer, -1, (LPWSTR)pUnicode, unicodeLen); 

	targetLen = WideCharToMultiByte(targetCodepage, 0, (LPWSTR)pUnicode, -1, 
		pTargetData, 0, NULL, NULL);
	pTargetData = (char *)malloc((targetLen+1) * sizeof(char));
	memset(pTargetData, 0, targetLen+1);
	WideCharToMultiByte(targetCodepage, 0, (LPWSTR)pUnicode, -1, 
		pTargetData, targetLen, NULL, NULL);

	free(pUnicode);
	return pTargetData; 
}
/*
* function: decide weather the input buffer is UTF-8 without BOM
* para: 
*      pBuffer  the pointer point to the buffer which store the string
*      size     the length of buffer in byte
* return:
*       true  the input string is UTF-8 without BOM
*       false **************** is not UTF-8 without BOM
*/
bool TxtEncoder::IsUTF8NOBOM(long size)
{
	bool IsUTF8 = true;
	unsigned char* start = (unsigned char*)m_pbuffer;
	unsigned char* end = (unsigned char*)m_pbuffer + size;
	while(start<end)
	{
		if (*start<0x80)
			start++;
		else if (*start<0xC0)
		{
			IsUTF8=false;
			break;
		}
		else if (*start<0xE0)
		{
			if (start>=end-1)
				break;
			if ((start[1]&0xC0)!=0x80)
			{
				IsUTF8 = false;
				break;
			}
			start +=2;
		}
		else if (*start<0xF0)
		{
			if(start>=end-2)
				break;
			if (((start[1]&0xC0)!=0x80)||((start[2]&0xC0)!=0x80))
			{
				IsUTF8 = false;
				break;
			}
			start +=3;
		}
		else if (*start<0xF7)
		{
			if (start>end-3)
				break;
			if ((start[1]&0xC0)!=0x80 || (start[2]&0xC0)!=0x80 || (start[3]&0xC0)!=0x80)
			{
				IsUTF8 = false;
				break;
			}
			start+=4;
		}
		else
		{
			IsUTF8 = false;
			break;
		}
	}
	return IsUTF8;
}
/*
* function: decide weather the input buffer is UTF-16 without BOM
* para: 
*      pBuffer  the pointer point to the buffer which store the string
*      size     the length of buffer in byte
* return:
*       true  the input string is UTF-16 without BOM
*       false **************** is not UTF-16 without BOM
*/
bool TxtEncoder::IsUTF16NOBOM(long bytesize)
{
	bool IsUTF16NOBOM=true;
	for (long i=1;i<bytesize;i+=2)
	{
		if (m_pbuffer[i]<=0xD7)
			continue;
		else if ((m_pbuffer[i]<=0xFF)&&(m_pbuffer[i]>=0xE0))
			continue;
		else if ((m_pbuffer[i]<=0xDB)&&(m_pbuffer[i]>=0xD8))
		{
			i+=2;
			if ((m_pbuffer[i]<=0xDF)&&(m_pbuffer[i]>=0xDC))
				continue;
			else 
			{
				IsUTF16NOBOM = false;
				break;
			}
		}
		else
		{
			IsUTF16NOBOM = false;
			break;
		}
	}
	return IsUTF16NOBOM;
}

/*
* function: convert big-endia to little-endia
* para:
*     pBuffer: the pointer that point to the wide char buffer
*     size: the length of the buffer in double byte
*/
void TxtEncoder::BigEndiaToLittleEndia(TCHAR * pBuffer, long size)
{
	TCHAR tmp=0x00000000;
	for (int i=0;i<size;i++)
	{
		tmp=(pBuffer[i]&0x0000FFFF)<<8;
		pBuffer[i]=(pBuffer[i]>>8)|tmp;
	}
}

string TxtEncoder::wstring2string(wstring ws)
{
	string curLocale = setlocale(LC_ALL, NULL);        // curLocale = "C";
	setlocale(LC_ALL, "chs");
	const wchar_t* _Source = ws.c_str();
	size_t _Dsize = 2 * ws.size() + 1;
	char *_Dest = new char[_Dsize];
	memset(_Dest,0,_Dsize);
	wcstombs(_Dest,_Source,_Dsize);
	string result = _Dest;
	delete []_Dest;
	setlocale(LC_ALL, curLocale.c_str());
	return result;
}
/*
* function: get the text file's coding type
* if the text file has BOM, decide the coding type by BOM
* para: 
*     pBuffer: the pointer of the buffer;
*     bytesize: the length of the buffer;
* return: CODINGTYPE can have five selection
*       ASCII 1
*       UTF8 2
*       UTF8NOBOM 3
*       UTF16 4
*       UTF16BIGENDIA 5
*/
CODINGTYPE TxtEncoder::GetCodingType(long bytesize)
{
	unsigned char* tempbuffer = (unsigned char*)m_pbuffer;
	if (bytesize>=2)
	{
		if ((0xFF==tempbuffer[0])&&(0xFE==tempbuffer[1]))
			return UTF16;
		else if ((0xFE==tempbuffer[0])&&(0xFF==tempbuffer[1]))
			return UTF16BIGENDIA;
	}
	if (bytesize>=3)
		if (0xEF==tempbuffer[0] && 0xBB==tempbuffer[1] && 0xBF==tempbuffer[2])
			return UTF8;
	if (IsUTF8NOBOM(bytesize))
		return UTF8NOBOM;
	else
		return ASCII;

}

/*
* function: read file into buffer
* the buffer is denoted by a unsigned char * tempchar;
* bytesize: denote the byte length of the buffer
*/
void TxtEncoder::ReadFileIntoRam(FILE* plrcfile, string& alllrc)
{
	CODINGTYPE codetype=NOTSURE;
	unsigned char* tempchar=NULL;
	long bytesize=0;
	fseek(plrcfile,0,SEEK_END);
	bytesize=ftell(plrcfile);
	tempchar = new unsigned char[bytesize+2];
	memset(tempchar,0,bytesize+2);
	rewind(plrcfile);
	fread(tempchar,sizeof(char),bytesize,plrcfile);
	fclose(plrcfile);

	this->SetBuffer((char*)tempchar, bytesize+2);
	codetype = this->GetCodingType(bytesize);
	if (UTF16==codetype)
	{
		wstring tempstring;
		tempstring +=(WCHAR*)tempchar;
		alllrc = TxtEncoder::wstring2string(tempstring);

	}
	else if (UTF8==codetype || UTF8NOBOM ==codetype)
	{
		alllrc = this->Convert(CP_UTF8, CP_ACP);
	}
	else if (ASCII==codetype)
	{
		alllrc+=(char*)tempchar;
	}
	else if(UTF16BIGENDIA==codetype)
	{
		TxtEncoder::BigEndiaToLittleEndia((TCHAR*)tempchar, (bytesize+2)/2);
		wstring tempstring;
		tempstring +=(WCHAR*)tempchar;
		alllrc = TxtEncoder::wstring2string(tempstring);
	}
	delete [] tempchar;

}

LIBLYRICS_NAMESPACE_END